package core.protocol.impl;

import java.rmi.RemoteException;
import java.util.TreeSet;

import core.api.ClientAPI;
import core.protocol.ProtocolAD;

public class ChangRoberts implements ProtocolAD{

	private enum State{
		INIT,
		LEADER,
		LOST,
		SLEEP;
	}

	private final ClientAPI client ;
	State state;
	int myNumber,next;
	Integer token;
	private final TreeSet<Integer> neighbors= new TreeSet<Integer>();

	public static final int tempo = 5000;

	public ChangRoberts(final ClientAPI client){
		super();

		this.client = client;
		try {
			myNumber = client.getId();
			token = myNumber;
			next = -1;
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		state = State.SLEEP;
	}

	@Override
	public void addNeighbour(int i){
		neighbors.add(i);

		//Récupération du premier noeud (fin de boucle)
		Integer high = neighbors.higher(myNumber); 
		if( high != null ){
			//Récupération du noeud suivant
			next = high;
			System.out.println(myNumber + " Changement de suivant  : " + next);
		}else{ 
			//Récupération du premier noeud (fin de boucle)
			next = neighbors.first();
			System.out.println(myNumber + " Changement de suivant  : " + next);
		}
	}

	@Override
	public void removeNeighbour(int i){
		neighbors.remove(i);
		if( i == next ){
			Integer high = neighbors.higher(myNumber); 
			if( high != null ){
				//Récupération du noeud suivant
				next = high;
				System.out.println(myNumber + " Changement de suivant  : " + next);
			}else{ 
				//Récupération du premier noeud (fin de boucle)
				next = neighbors.first();
				System.out.println(myNumber + " Changement de suivant  : " + next);
			}
		}
	}

	@Override
	public void timeoutProtocol(int dest) {
		if(lastMethod!=null && lastObjects!=null){
			try {
				client.callProtocol(myNumber,next,lastMethod,lastObjects);
			} catch (RemoteException e3) {
				e3.printStackTrace();
			}
		}
	}

	@Override
	public void run() {
		startElection();
	}

	private String lastMethod = null;
	private Object []lastObjects = null;
	
	private void registerLastSend(String method, Object... objects){
		lastMethod = method;
		lastObjects = objects;
	}
	
	public void sendToken(Integer token){
		System.out.println("Received token " + token);

		//this.notify();
		
		if(state == State.INIT){
			if( token == myNumber ){
				state = State.LEADER;
				try {
					client.setMaster(myNumber,myNumber,myNumber);
				} catch (RemoteException e) {
					e.printStackTrace();
				}
				try {
					client.callProtocol(myNumber, next,"elected",token);
					registerLastSend("elected",token);
				} catch (RemoteException e3) {
					e3.printStackTrace();
				}
			}else if( token > myNumber ){
				if(state == State.INIT)
					state = State.LOST;
				this.token = token;
				try {
					client.callProtocol(myNumber, next,"sendToken",token);
					registerLastSend("sendToken",token);
				} catch (RemoteException e3) {
					e3.printStackTrace();
				}
			}
			//this.notify();
		}else{
			try {
				client.callProtocol(myNumber, next, "sendToken", token);
				registerLastSend("sendToken", token);
			} catch (RemoteException e3) {
				e3.printStackTrace();
			}
			if(state == State.SLEEP)
				state = State.LOST;
		}
	}

	public void elected(Integer token){
		System.out.println("Received elected " + token);

		//notify();
		
		if(state != State.LEADER){
			try {
				client.callProtocol(myNumber, next, "elected", token);
			} catch (RemoteException e3) {
			}
			try {
				client.setMaster(myNumber,myNumber,token);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}
	
	@Override
	public void startElection() {
		System.out.println("Process " + myNumber + " starts election ");
		state = State.INIT;

		try {
			client.callProtocol(myNumber, next,"sendToken",token);
		} catch (RemoteException e3) {
		}
	}

}
